﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
using Quartz;
using Anay.TaskScheduling.Common;
using Anay.TaskScheduling.JobConfig;
using Anay.TaskScheduling.Model;

namespace Anay.TaskScheduling
{
    /// <summary>
    /// 实现数据任务任务.
    /// 注入到调度小程序中.
    /// </summary>
    public class UploadJob : IJob
    {
        public DateTime? startTime = null;
        public DateTime? endTime = null;
        public static bool isLock = false;
        public static object lock_obj = new object();
        public string ConfigId = string.Empty;
        private const string ServerPassword = "hdkm123"; // 服务端口令
        private static readonly IConfigHelper ConfigHelper = new ConfigFactory().GetInstance();
        private readonly List<ExtractConfig> extConfigList = new List<ExtractConfig>();
        private readonly Dictionary<string, Action<List<ExtractConfig>, Ep_task_config, Action<string>>> extractHandlers =
            new Dictionary<string, Action<List<ExtractConfig>, Ep_task_config, Action<string>>>();

        public UploadJob()
        {
            try
            {
                extractHandlers.Add("01", (l, e, a) => { ExtractOne(l, e, a); });
                extractHandlers.Add("02", (l, e, a) => { ExtractTwo(l, e, a); });
                extractHandlers.Add("03", (l, e, a) => { ExtractThree(l, e, a); });

                // 载入指令配置文件
                string filepath = AppDomain.CurrentDomain.BaseDirectory + "ExtractConfig.json";
                extConfigList = JsonConvert.DeserializeObject<List<ExtractConfig>>(File.ReadAllText(filepath));
            }
            catch (Exception ex)
            {
                Logging.LogHelper.Write("获取ExtractConfig.json异常：" + ex.Message + "\r\n" + ex.StackTrace);
            }
        }

        public async Task Execute(IJobExecutionContext context)
        {
            await Task.Run(() =>
            {
                while (isLock)
                {
                    System.Threading.Thread.Sleep(5000);
                }

                //用于线程安全
                lock (lock_obj)
                {
                    isLock = true;
                }

                Ep_task_config task_Config = new Ep_task_config();
                try
                {
                    try
                    {
                        JobDataMap jobDataMap = context.JobDetail.JobDataMap;
                        ConfigId = jobDataMap.GetString("ConfigId");
                        task_Config = ConfigHelper.GetTask_config(ConfigId);
                    }
                    catch (Exception ex)
                    {
                        Logging.LogHelper.Write("执行job时（获取数据库job配置）异常：" + ex.Message + "\r\n" + ex.StackTrace);
                    }

                    if (task_Config != null && task_Config.IsEnabled.Trim() == "0")
                    {
                        try
                        {
                            //运行业务逻辑
                            Executeing(task_Config, context).GetAwaiter().GetResult();
                        }
                        catch (Exception ex)
                        {

                            Logging.LogHelper.Write("执行job时（运行业务逻辑）异常：" + ex.Message + "\r\n" + ex.StackTrace);
                        }

                    }
                }
                catch (Exception ex)
                {

                    Logging.LogHelper.Write("执行job时（整体业务逻辑）：" + ex.Message + "\r\n" + ex.StackTrace);
                }
                finally
                {
                    task_Config.UpdateDate = DateTime.Now;
                    ConfigHelper.UpdateLastRunTime(task_Config);
                    isLock = false;
                }
            });
        }

        /// <summary>
        /// 开始执行业务操作.
        /// </summary>
        /// <param name="task_Config">配置文件.</param>
        /// <param name="context"></param>
        /// <returns></returns>
        public virtual async Task Executeing(Ep_task_config task_Config, IJobExecutionContext context, Action<string> logAction = null)
        {
            await Task.Run(() =>
            {
                string log = task_Config.JobName + "JOB开始：" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n";
                logAction?.Invoke(log);
                WriteLogaAsync(task_Config, log);
                HttpHelper httpHelper = new HttpHelper();
                var jobConfs = extConfigList.Where(o => o.Type == task_Config.Id).ToList();
                if (jobConfs != null && jobConfs.Count > 0)
                {
                    if (extractHandlers.ContainsKey(task_Config.Id))
                    {
                        extractHandlers[task_Config.Id](jobConfs, task_Config, logAction);
                    }
                    else
                    {
                        log = $"程序未实现类型{task_Config.Id}的处理任务，请联系开发人员\r\n";
                        Logging.LogHelper.Write(log);
                        logAction?.Invoke(log);
                    }
                }

                log = task_Config.JobName + "JOB结束：" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n";
                logAction?.Invoke(log);
                WriteLogaAsync(task_Config, log);
            });
        }

        /// <summary>
        /// 类型01的数据抽取任务实现.
        /// 【每天有效记录只有1条，当天保持最新】如：当日总电量
        /// </summary>
        /// <param name="list"></param>
        /// <param name="config"></param>
        /// <param name="logAction">对外写日志</param>
        private void ExtractOne(List<ExtractConfig> list, Ep_task_config config, Action<string> logAction)
        {
            string log = string.Empty;
            List<ExtractData> uploadData = new List<ExtractData>();
            PIServerHelper pIServer = new PIServerHelper();
            var nowTime = DateTime.Now;
            config.LastRunTime = nowTime;
            var startTime = ConfigHelper.GetLastRunTime(config.Id) ?? Convert.ToDateTime(nowTime.ToString("yyyy-MM-dd") + " 00:00:00"); // 如未上次结束时间，则以当天0点开始
            foreach (var item in list)
            {
                try
                {
                    // PI数据服务器时间和站点服务器会有时间差，这个时间差不确定，暂以加延后十分钟的查询记录
                    var ls = pIServer.GetHistoryDataPI(item.Tagid, startTime.AddMinutes(-10), nowTime); 
                    if (ls != null && ls.Count > 0)
                    {
                        // Linq查询每天最后一条记录
                        var r = from l in ls
                                group l by l.DateTime.ToString("yyyy-MM-dd") into g
                                select g.OrderByDescending(x => x.DateTime).FirstOrDefault();
                        uploadData.AddRange(r);
                    }
                }
                catch (Exception ex)
                {
                    log = $"抽取{item.Tagid}异常：{ex.Message}\r\n";
                    Logging.LogHelper.Write(log);
                    logAction?.Invoke(log);
                    continue;
                }
            }

            // 后期可考虑分组发送， 即当次发送列表超过5k个要能自动分组发送
            Dictionary<string, string> dictionary = new Dictionary<string, string>()
            {
                { "password", ServerPassword },
                { "data", JsonConvert.SerializeObject(uploadData) }
            };
            HttpGo(() =>
            {
                return HttpHelper.PostAsync(config.JobUrl, dictionary, Encoding.UTF8);
            }, config);
        }

        /// <summary>
        /// 类型02的数据抽取任务的实现.
        /// 【每隔指定时间抽一次，取每次抽取的最新记录保存】如：实时负荷量
        /// </summary>
        /// <param name="list"></param>
        /// <param name="config"></param>
        /// <param name="logAction">对外写日志</param>
        private void ExtractTwo(List<ExtractConfig> list, Ep_task_config config, Action<string> logAction)
        {
            string log = string.Empty;
            List<ExtractData> uploadData = new List<ExtractData>();
            PIServerHelper pIServer = new PIServerHelper();
            var nowTime = DateTime.Now;
            config.LastRunTime = nowTime;
            var startTime = ConfigHelper.GetLastRunTime(config.Id) ??
                Convert.ToDateTime(nowTime.ToString("yyyy-MM-dd") + " 00:00:00"); // 如未上次结束时间，则以当天0点开始
            TimeSpan ts = nowTime - startTime;
            int instaTime = int.TryParse(config.TimeExpression, out instaTime) ? instaTime : 300; // 默认5min
            foreach (var item in list)
            {
                try
                {
                    DateTime flagTime = startTime; // DateTime是值类型
                    for (int i = 0; i < ts.TotalSeconds; i += instaTime)
                    {
                        var ls = pIServer.GetHistoryDataPI(item.Tagid, flagTime, nowTime);
                        flagTime = startTime.AddSeconds(instaTime);
                        if (ls != null && ls.Count > 0)
                        {
                            // 每隔指定时间内的最后一条记录加到返回列表中
                            var obj = ls.OrderByDescending(x => x.DateTime).FirstOrDefault();
                            obj.Name = item.Name;
                            uploadData.Add(obj);
                        }
                    }
                }
                catch (Exception ex)
                {
                    log = $"抽取{item.Tagid}异常：{ex.Message}\r\n";
                    Logging.LogHelper.Write(log);
                    logAction?.Invoke(log);
                    continue;
                }
            }

            // 后期可考虑分组发送， 即当次发送列表超过5k个要能自动分组发送
            HttpGo(() =>
            {
                return HttpHelper.PostAsync(config.JobUrl, new Dictionary<string, string>()
                {
                    { "password", ServerPassword },
                    { "data", JsonConvert.SerializeObject(uploadData) }
                }, Encoding.UTF8);
            }, config);
        }

        /// <summary>
        /// 类型03的数据抽取任务的实现.
        /// 【有且只有一条有效记录】如：经济指标
        /// </summary>
        /// <param name="list"></param>
        /// <param name="config"></param>
        /// <param name="logAction">对外写日志</param>
        private void ExtractThree(List<ExtractConfig> list, Ep_task_config config, Action<string> logAction)
        {
            string log = string.Empty;
            List<ExtractData> uploadData = new List<ExtractData>();
            PIServerHelper pIServer = new PIServerHelper();
            var nowTime = DateTime.Now;
            config.LastRunTime = nowTime;
            var startTime = ConfigHelper.GetLastRunTime(config.Id) ?? Convert.ToDateTime(nowTime.ToString("yyyy-MM-dd") + " 00:00:00"); // 如未上次结束时间，则以当天0点开始
            foreach (var item in list)
            {
                try
                {
                    var ls = pIServer.GetHistoryDataPI(item.Tagid, startTime, nowTime);
                    if (ls != null && ls.Count > 0)
                    {
                        // 时间段内的最后一条
                        var obj = ls.OrderByDescending(x => x.DateTime).FirstOrDefault();
                        obj.Name = item.Name;
                        uploadData.Add(obj);
                    }
                }
                catch (Exception ex)
                {
                    log = $"抽取{item.Tagid}异常：{ex.Message}\r\n";
                    Logging.LogHelper.Write(log);
                    logAction?.Invoke(log);
                    continue;
                }
            }

            // 后期可考虑分组发送
            HttpGo(() =>
            {
                return HttpHelper.PostAsync(config.JobUrl, new Dictionary<string, string>()
                {
                    { "password", ServerPassword },
                    { "data", JsonConvert.SerializeObject(uploadData) }
                }, Encoding.UTF8);
            }, config);
        }

        public static Task HttpGo(Func<Task<HttpResult>> func, Ep_task_config _Task_Config)
        {
            return Task.Run(async () =>
            {
                var result = await func();
                string path = AppDomain.CurrentDomain.BaseDirectory + @"/Log/TaskResult/" + _Task_Config.JobName + "/";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                path += _Task_Config.JobName + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
                FileInfo file = new FileInfo(path);
                if (!file.Exists)
                {

                    FileStream fs;
                    fs = File.Create(path);
                    fs.Close();
                }

                using (FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write))
                {
                    using (StreamWriter sw = new StreamWriter(fs))
                    {
                        sw.WriteLine(DateTime.Now.ToString() + "\r\n" + result.html + "\r\n\r\n");
                    }
                }
            });
        }

        /// <summary>
        /// 异步日志
        /// </summary>
        /// <param name="task_Config"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public async Task WriteLogaAsync(Ep_task_config task_Config, string msg)
        {
            await Task.Run(() =>
            {
                WriteLog(task_Config, msg);
            });
        }

        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="task_Config"></param>
        /// <param name="msg"></param>
        public void WriteLog(Ep_task_config task_Config, string msg)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + @"/Log/TaskRun/" + task_Config.JobName + "/";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            path += task_Config.JobName + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
            FileInfo file = new FileInfo(path);
            if (!file.Exists)
            {

                FileStream fs;
                fs = File.Create(path);
                fs.Close();
            }

            using (FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write))
            {
                using (StreamWriter sw = new StreamWriter(fs))
                {
                    sw.WriteLine(msg);
                }
                fs.Close();
            }
        }
    }
}
